/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System Driver Module                                  */
/*                                                                           */
/*    Copyright (C) 2011 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------     */
/*    MRF49XA driver                                                         */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Includes
#include <drvMRF49XA.h>
#include <krnlSystemTimer.h>
#include "drvIOConfig.h"

///////////////////////////////////////////////////////////////////////////////
// Constants

// Bit position:   7  6  5  4  3  2  1  0
// Normal modes:                 <X  X  X>
// Testing modes: <X  X  X>                   

// Normal modes
#define drvMRF_IDLE							0x00	// Listening for packets, nothing yet
#define drvMRF_TRANSMIT_PACKET	0x01	// Actively transmitting a packet
#define drvMRF_RECEIVE_PACKET		0x02	// Actively receiving a packet

// Testing modes
#define drvMRF_RECEIVE_ALL			0x20	// Not yet implemented
#define drvMRF_TRANSMIT_ZERO		0x40	// Transmits all '0'
#define drvMRF_TRANSMIT_ONE			0x80	// Transmits all '1'
#define drvMRF_TRANSMIT_ALT			0xC0	// Transmits alternating '01'

#define drvMRF_TX_TEST_MASK			0xC0	// Singles out spectrum test modes

#if  defined(drvMRF_BAND_434)
    #define drvMRF_FREQ_Band        0x0010              			//434MHz
    #define drvMRF_CFSREG_SET       (drvMRF_CFSREG | 0x0640)	// Center Frequency: 434MHz
#elif  defined(drvMRF_BAND_868)
    #define drvMRF_FREQ_Band        0x0020              			//868MHz
    #define drvMRF_CFSREG_SET       (drvMRF_CFSREG | 0x0640)	// Center Frequency: 868MHz
#elif  defined(drvMRF_BAND_915)
    #define drvMRF_FREQ_Band        0x0030										//915MHz
    #define drvMRF_CFSREG_SET       (drvMRF_CFSREG | 0x07D0)	// Center Frequency: 915.000MHz
#else
    #error  "At least one frequency band must be defined"
#endif

// predefine chip register values
#define drvMRF_GENCREG_SET			(drvMRF_GENCREG | drvMRF_XTAL_LD_CAP_125 | drvMRF_FREQ_Band)
#define drvMRF_AFCCREG_SET			(drvMRF_AFCCREG | drvMRF_AUTOMS_INDP | drvMRF_ARFO_15to16 | drvMRF_HAM | drvMRF_FOREN | drvMRF_FOFEN)

#define drvMRF_RXCREG_SET      (drvMRF_RXCREG | drvMRF_FINTDIO | drvMRF_RXBW_67K | drvMRF_DRSSIT_103db)
#define drvMRF_TXCREG_SET      (drvMRF_TXCREG | drvMRF_MODBW_30K | drvMRF_OTXPWR_0)
#define drvMRF_DRSREG_SET      	(drvMRF_DRSREG | (drvMRF_DRPV_VALUE & drvMRF_DRPV_MASK))
#define drvMRF_FIFOSTREG_SET		(drvMRF_FIFORSTREG | drvMRF_DRSTM | ((8 << 4) & drvMRF_FFBC_MASK))

//#define MRF_GENCREG_SET			(MRF_GENCREG | (MRF_LCS & MRF_LCS_MASK) | MRF_FBS_434)
//#define MRF_PLLCREG_SET			(MRF_PLLCREG | MRF_CBTC_5p)
//#define MRF_CFSREG_SET      (MRF_CFSREG | (MRF_FREQB & MRF_FREQB_MASK))

//#define drvMRF_AFCCREG_SET             0xC4F7
//#define drvMRF_TXCREG_SET              0x9850          // Deviation: 75kHz
//#define drvMRF_TXBREG_SET              0xB800
//#define drvMRF_RXCREG_SET              0x9481          // BW 200KHz, LNA gain 0dB, RSSI -97dBm
//#define drvMRF_FIFORSTREG_SET          0xCA81
//#define drvMRF_DRVSREG_SET             0xC623          // Data Rate:9579Baud
//#define drvMRF_PMCREG_SET              0x8201


///////////////////////////////////////////////////////////////////////////////
// Module variables

// RX variables
static drvMRFPacket l_rx_buffer_1;								// There are 2 Rx_Packet_t instances, one for reading off the air
static drvMRFPacket l_rx_buffer_2;								// and one for processing back in main.
static volatile dosBool	l_packet_received;				// The l_packet_received flag means that the l_received_packet variable contains
static drvMRFPacket* l_received_packet;						// a fresh data packet.  The l_receiving_packet always contains space for
static drvMRFPacket* l_receiving_packet;					// received data.

// TX variables
static volatile dosByte l_transmit_buffer[drvMRF_TX_PACKET_LEN];							// transmit data buffer
static volatile dosByte l_transmit_index = 0;																	// transmit byte index

// Other variables
static volatile dosBool l_access_lock = dosFalse;															// lock chip access if true
static volatile dosBool l_interrupt_pending = dosFalse;												// true if interrupt occured while access was locked
static volatile dosByte l_state;																							// driver state

///////////////////////////////////////////////////////////////////////////////
// Local functions declaration
static void SetRegister(dosWord in_setting);
static dosWord ReadStatus(void);
static void LockAccess(void);
static void UnlockAccess(void);
static dosByte ReadData(void);

///////////////////////////////////////////////////////////////////////////////
// Initializes MRF49 device
void drvMRFInit(void)
{
	dosWord status;
	
	// init variables
	l_access_lock = dosFalse;
	l_interrupt_pending = dosFalse;
	
	// init pins
	InitMRFCS();
	InitMRFINT();
	InitMRFSPI();
	
	// wait 100ms for the Power-On-Reset
	krnlDelay(100);
	
	status = ReadStatus();
	
	// configuring the MRF49XA radio
	SetRegister(drvMRF_FIFOSTREG_SET);												// Set 8 bit FIFO interrupt count
	SetRegister(drvMRF_FIFOSTREG_SET | drvMRF_FSCF);					// Enable sync. latch
	SetRegister(drvMRF_GENCREG_SET);
	SetRegister(drvMRF_AFCCREG_SET);
	SetRegister(drvMRF_CFSREG_SET);
	SetRegister(drvMRF_DRSREG_SET);														// Approx. 9600 baud
	SetRegister(drvMRF_PMCREG | drvMRF_CLKODIS);							// Shutdown everything
	SetRegister(drvMRF_RXCREG_SET);
	SetRegister(drvMRF_TXCREG_SET);
	SetRegister(drvMRF_BBFCREG | drvMRF_ACRLC | (4 & drvMRF_DQTI_MASK));

  // antenna tuning on startup
	SetRegister(drvMRF_PMCREG | drvMRF_CLKODIS | drvMRF_TXCEN); 	// turn on the transmitter
  krnlDelay(5);																									// wait for oscillator to stablize
	// end of antenna tuning
	
	// turn off transmitter, turn on receiver
	SetRegister(drvMRF_PMCREG | drvMRF_CLKODIS | drvMRF_RXCEN);
	SetRegister(drvMRF_GENCREG_SET | drvMRF_FIFOEN);
	SetRegister(drvMRF_FIFOSTREG_SET);
	SetRegister(drvMRF_FIFOSTREG_SET | drvMRF_FSCF);
	
	// Setup the packet pointers
	l_receiving_packet = &l_rx_buffer_1;
	
	// Dummy read of status registers to clear Power on reset flag
	status = ReadStatus();
	
	sysNOP();
}

///////////////////////////////////////////////////////////////////////////////
// Resets MRF49XA
void drvMRFReset(void)
{
	SetRegister(drvMRF_PMCREG);
	SetRegister(drvMRF_FIFOSTREG_SET);
	SetRegister(drvMRF_GENCREG_SET);
	SetRegister(drvMRF_GENCREG_SET | drvMRF_FIFOEN);
	SetRegister(drvMRF_FIFOSTREG_SET | drvMRF_FSCF);
	SetRegister(drvMRF_PMCREG | drvMRF_RXCEN);	
}

///////////////////////////////////////////////////////////////////////////////
// Interrupt handler
void drvMRFInterruptHandler(void)
{
	dosByte byte_buffer;
	dosWord state;

	// check for access lock
	if( l_access_lock )
	{
		// access is locked -> exit interrupt handler
		l_interrupt_pending = dosTrue;
		return;
	}
	
	// Read status word
	state = ReadStatus();
	
	// Perform operations
	if ((state & drvMRF_TXRXFIFO) != 0) 
	{
		switch (l_state) 
		{
			// idle state -> incoming message
			case drvMRF_IDLE:
				byte_buffer = ReadData();

				// The first byte is the length field
				if (byte_buffer > 0 && byte_buffer <= drvMRF_PAYLOAD_LEN)
				{
					l_state = drvMRF_RECEIVE_PACKET;
					l_receiving_packet->length = byte_buffer;
					l_transmit_index = 0;
				} 
				else
				{
					// The length field was obviously bogus, reset the module
					l_transmit_index = 0;
					l_receiving_packet->length = 0;
					drvMRFReset();
					return;
				}
				break;

			// transmit packet
			case drvMRF_TRANSMIT_PACKET:
				// Transmit the contents of the packet
				// (including preamble, sync, length, and dummy byte)
				SetRegister(drvMRF_TXBREG | l_transmit_buffer[l_transmit_index++]);
				
				// Derivation of the +6:
				// Preamble:      1 + (before packet)
				// Sync word:     2 + (before packet)
				// Length byte:   1 + (Packet byte 1, not included in length)
				// Dummy byte:    1 + (Not actually transmitted)
				// Pre-increment: 1 =
				// Total:         6
				if (l_transmit_index >= (l_transmit_buffer[3] + 6)) 
				{
					// Disable transmitter, enable receiver
					SetRegister(drvMRF_PMCREG | drvMRF_RXCEN);
					SetRegister(drvMRF_GENCREG_SET | drvMRF_FIFOEN);
					SetRegister(drvMRF_FIFOSTREG_SET);
					SetRegister(drvMRF_FIFOSTREG_SET | drvMRF_FSCF);
					
					// Return the state
					l_state = drvMRF_IDLE;
					l_transmit_index = 0;
				}
				break;

			// packet receiving								
			case drvMRF_RECEIVE_PACKET:	// We've received at least the size
				l_receiving_packet->payload[l_transmit_index++] = ReadData();
				
				// End of packet?
				if (l_transmit_index >= l_receiving_packet->length)
				{
					// Reset the FIFO
					SetRegister(drvMRF_FIFOSTREG_SET);
					SetRegister(drvMRF_FIFOSTREG_SET | drvMRF_FSCF);
					
					// Store received packet (or throw it away if there is a received packet already)
					if( !l_packet_received )
					{
						l_received_packet = l_receiving_packet;
						l_packet_received = dosTrue;
						
						if (l_receiving_packet == &l_rx_buffer_1) 
						{
							l_receiving_packet = &l_rx_buffer_2;
						}
						else 
						{
							l_receiving_packet = &l_rx_buffer_1;
						}
					}
					
					// Restore state
					l_state = drvMRF_IDLE;
					l_transmit_index = 0;
					l_receiving_packet->length = 0;
				}			
				break;

#ifdef drvMRF_TEST_MODE
			// transmiting zero
			case drvMRF_TRANSMIT_ZERO:
				SetRegister(drvMRF_TXBREG | 0x0000);
				break;
				
			// transmitting one
			case drvMRF_TRANSMIT_ONE:
				SetRegister(drvMRF_TXBREG | 0x00FF);
				break;
				
			// transmitting alternating zero-one
			case drvMRF_TRANSMIT_ALT:
				SetRegister(drvMRF_TXBREG | 0x00AA);
				break;
#endif

			default:
				break;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Returns received packet (NULL if no packet received)
drvMRFPacket* drvMRFGetReceivedPacket()
{
	if (l_packet_received) 
	{
		l_packet_received = dosFalse;
		return l_received_packet;
	}
	else
	{
		return DOS_NULL;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Returns true if chip is idle
dosBool drvMRFIsIdle(void)
{
	if (l_state == drvMRF_IDLE) 
	{
		return dosTrue;
	}
	else 
	{
		return dosFalse;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Returns tru8e if packet is received
dosBool drvMRFIsPacketReceived(void)
{
	return l_packet_received;
}

///////////////////////////////////////////////////////////////////////////////
// Starts packet transmission (returns true if success)
dosBool drvMRFTransmitPacket(drvMRFPacket *packet)
{
	dosByte	i;
	dosBool success = dosFalse;
	dosWord state;

#ifdef drvMRF_TEST_MODE
	// We can check, without synchronization
	// (because it doesn't change in the ISR)
	// Whether we're in a testing mode.  If so, simply return.
	// This is important, so we don't hard lock.
	if (l_state & drvMRF_TX_TEST_MASK)
	{
		return dosFalse;
	}
#endif

	// lock module access
	LockAccess();
	
	state = ReadStatus();

	// check state
	if (l_state == drvMRF_IDLE)
	{
		l_state = drvMRF_TRANSMIT_PACKET;
	
		// Initialize the constant parts of the transmit buffer
		l_transmit_index = 0;
		l_transmit_buffer[0] = 0xAA;
		l_transmit_buffer[1] = 0x2D;
		l_transmit_buffer[2] = 0xD4;
		l_transmit_buffer[3] = packet->length;

		// Copy the packet contents into the buffer
		for (i = 0; i < packet->length; i++)
		{
			l_transmit_buffer[i + 4] = packet->payload[i];
		}

		SetRegister(drvMRF_PMCREG);											// Turn everything off
		SetRegister(drvMRF_GENCREG_SET | drvMRF_TXDEN);	// Enable TX FIFO
		// Reset value of TX FIFO is 0xAAAA
		
		SetRegister(drvMRF_PMCREG | drvMRF_TXCEN);		// Begin transmitting
		// Everything else is handled in the ISR
	}
	
	// unlock chip access and return with success state
	UnlockAccess();
	
	return success;
}

/*****************************************************************************/
/* Local Functions                                                           */
/*****************************************************************************/

#ifdef _PC_DRIVER
///////////////////////////////////////////////////////////////////////////////
// Sets register
static void SetRegister(dosWord in_setting)
{
	drvSPISendAndReceiveWord( in_setting );
}

///////////////////////////////////////////////////////////////////////////////
// Reads status register
static dosWord ReadStatus(void)
{
	dosWord retval;
	
	retval = drvSPISendAndReceiveWord(drvMRF_STSREG);

	return retval;
}

///////////////////////////////////////////////////////////////////////////////
// Reads received data register
static dosByte ReadData(void)
{
	dosByte retval;

	retval = drvSPISendAndReceiveWord(drvMRF_RXFIFOREG) & 0xff;

	return retval;
}

#else
///////////////////////////////////////////////////////////////////////////////
// Sets register
static void SetRegister(dosWord in_setting)
{
	MRFCS(PIN_LOW);
	MRFSendReceiveByte(HIGH(in_setting));
	MRFSendReceiveByte(LOW(in_setting));
	MRFCS(PIN_HIGH);

	sysNOP();
	sysNOP();
}

///////////////////////////////////////////////////////////////////////////////
// Reads status register
static dosWord ReadStatus(void)
{
	dosWord retval;
	
	MRFCS(PIN_LOW);
	retval = ((dosWord)MRFSendReceiveByte(HIGH(drvMRF_STSREG))) << 8;
	retval += MRFSendReceiveByte(LOW(drvMRF_STSREG));
	MRFCS(PIN_HIGH);

	sysNOP();

	return retval;
}

///////////////////////////////////////////////////////////////////////////////
// Reads received data register
static dosByte ReadData(void)
{
	dosByte retval;
	
	MRFCS(PIN_LOW);
	MRFSendReceiveByte(HIGH(drvMRF_RXFIFOREG));
	retval = MRFSendReceiveByte(LOW(drvMRF_RXFIFOREG));
	MRFCS(PIN_HIGH);
	
	sysNOP();

	return retval;
}

#endif

///////////////////////////////////////////////////////////////////////////////
// Disable chip access for the interrupt routine
static void LockAccess(void)
{
	l_interrupt_pending = dosFalse;
	l_access_lock = dosTrue;
}

///////////////////////////////////////////////////////////////////////////////
// Enable chip access for the interrupt routine
static void UnlockAccess(void)
{
	// enable access
	l_access_lock = dosFalse;
	
	// call interrupt routine if interrupt is pending
	if( l_interrupt_pending )
	{
		l_interrupt_pending = dosFalse;

		drvMRFInterruptHandler();
	}
}


/*****************************************************************************/
/* Test Functions                                                            */
/*****************************************************************************/
#ifdef drvMRF_TEST_MODE

///////////////////////////////////////////////////////////////////////////////
// Trasmits zero continously
void drvMRFTransmitZero(void)
{
	// If we're already in a "dumb transmit" state, just change the value
	if (l_state & drvMRF_TX_TEST_MASK)
	{
		l_state = drvMRF_TRANSMIT_ZERO;
		return;
	}
	
	// Set state
	l_state = drvMRF_TRANSMIT_ZERO;

	// Enable the TX Register
	SetRegister(drvMRF_GENCREG_SET | drvMRF_TXDEN);
	
	// The transmit register is filled with 0xAAAA, we want it to be zeros
	SetRegister(drvMRF_TXBREG | 0x0000);
	
	// Enable the transmitter
	SetRegister(drvMRF_PMCREG | drvMRF_CLKODIS | drvMRF_TXCEN);
	
	// Upon completion of a byte !IRO should toggle
	return;	
}

///////////////////////////////////////////////////////////////////////////////
// Transmit one continously
void drvMRFTransmitOne(void)
{
	// If we're already in a "dumb transmit" state, just change the value
	if (l_state & drvMRF_TX_TEST_MASK)
	{
		l_state = drvMRF_TRANSMIT_ONE;
		return;
	}
	
	// set state
	l_state = drvMRF_TRANSMIT_ONE;
	
	// Enable the TX Register
	SetRegister(drvMRF_GENCREG_SET | drvMRF_TXDEN);
	
	// The transmit register is filled with 0xAAAA, we want it to be ones
	SetRegister(drvMRF_TXBREG | 0x00FF);
	
	// Enable the transmitter
	SetRegister(drvMRF_PMCREG | drvMRF_CLKODIS | drvMRF_TXCEN);
	
	// Upon completion of a byte !IRO should toggle
	return;	
}

///////////////////////////////////////////////////////////////////////////////
// Transmits alternating zero and one
void drvMRFTransmitAlternating()
{
	// If we're already in a "dumb transmit" state, just change the value
	if (l_state & drvMRF_TX_TEST_MASK)
	{
		l_state = drvMRF_TRANSMIT_ALT;
		return;
	}
	
	// set state
	l_state = drvMRF_TRANSMIT_ALT;
	
	// Enable the TX Register
	SetRegister(drvMRF_GENCREG_SET | drvMRF_TXDEN);
	
	// The transmit register is filled with 0xAAAA, we can leave it alone
	
	// Enable the transmitter
	SetRegister(drvMRF_PMCREG | drvMRF_CLKODIS | drvMRF_TXCEN);
	
	// Upon completion of a byte !IRO should toggle
	return;	
}
#endif

